/*-------------------------------------------------------------------------
 *
 * execJunk.c
 *	  垃圾属性支持功能....
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/execJunk.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "executor/executor.h"


/*-------------------------------------------------------------------------
 *		XXX 这些内容应重写以利用 ExecProject() 和 ProjectionInfo 节点。
 *			-cim 6/3/91
 *
 * 一个在执行器中存在的元组的属性，可能是一个正常属性或一个“垃圾”属性。“垃圾”属性
 * 永远不会从执行器中输出，即它们不会被打印、返回或存储在磁盘上。它们存在的唯一目的
 * 是存储一些仅对执行器有用的信息，主要是系统属性的值，如“ctid”或不需要输出的排序
 * 键列。
 *
 * 一般的想法如下：目标列表由包含表达式的 TargetEntry 节点列表组成。每个 TargetEntry
 * 都有一个名为 'resjunk' 的字段。如果此字段的值为 true，则相应的属性是一个“垃圾”
 * 属性。
 *
 * 当我们初始化一个计划时，我们调用 ExecInitJunkFilter 来创建一个过滤器。
 *
 * 然后我们执行计划，将 resjunk 属性视为其他属性。
 *
 * 最后，当在顶层返回一个元组时，我们可以调用 ExecFindJunkAttribute/ExecGetJunkAttribute
 * 来检索我们感兴趣的垃圾属性的值，并且调用 ExecFilterJunk 来从元组中移除所有
 * 垃圾属性。这个新的“干净”元组随后被打印、插入或更新。
 *
 *-------------------------------------------------------------------------
 */

/*
 * ExecInitJunkFilter
 *
 * 初始化垃圾过滤器。
 *
 * 源目标列表被传入。输出元组描述符由非垃圾目标列表条目构建。
 * 还可以传递一个可选的结果槽；否则，我们会创建一个。
 */
JunkFilter *
ExecInitJunkFilter(List *fc_targetList, TupleTableSlot *fc_slot)
{
	JunkFilter *fc_junkfilter;
	TupleDesc	fc_cleanTupType;
	int			fc_cleanLength;
	AttrNumber *fc_cleanMap;

	/*
	 * 计算干净元组的元组描述符。
	 */
	fc_cleanTupType = ExecCleanTypeFromTL(fc_targetList);

	/*
	 * 使用给定的槽，或者如果未给定则创建一个新槽。
	 */
	if (fc_slot)
		ExecSetSlotDescriptor(fc_slot, fc_cleanTupType);
	else
		fc_slot = MakeSingleTupleTableSlot(fc_cleanTupType, &TTSOpsVirtual);

	/*
	 * 现在计算原始元组属性与“干净”元组属性之间的映射。
	 *
	 * “映射”是一个“cleanLength”属性编号的数组，即“干净”元组的每个属性一个条目。
	 * 此条目的值是对应于“原始”元组相应属性的属性编号。（零表示 NULL 输出属性，
	 * 但我们在此例程中不使用此功能。）
	 */
	fc_cleanLength = fc_cleanTupType->natts;
	if (fc_cleanLength > 0)
	{
		AttrNumber	fc_cleanResno;
		ListCell   *fc_t;

		fc_cleanMap = (AttrNumber *) palloc(fc_cleanLength * sizeof(AttrNumber));
		fc_cleanResno = 0;
		foreach(fc_t, fc_targetList)
		{
			TargetEntry *fc_tle = lfirst(fc_t);

			if (!fc_tle->resjunk)
			{
				fc_cleanMap[fc_cleanResno] = fc_tle->resno;
				fc_cleanResno++;
			}
		}
		Assert(fc_cleanResno == fc_cleanLength);
	}
	else
		fc_cleanMap = NULL;

	/*
	 * 最后创建并初始化 JunkFilter 结构。
	 */
	fc_junkfilter = makeNode(JunkFilter);

	fc_junkfilter->jf_targetList = fc_targetList;
	fc_junkfilter->jf_cleanTupType = fc_cleanTupType;
	fc_junkfilter->jf_cleanMap = fc_cleanMap;
	fc_junkfilter->jf_resultSlot = fc_slot;

	return fc_junkfilter;
}

/*
 * ExecInitJunkFilterConversion
 *
 * 为行类型转换初始化 JunkFilter。
 *
 * 在这里，我们得到目标“干净”元组描述符，而不是从目标列表推断出来。目标描述符
 * 可以包含已删除的列。假设调用者已检查非删除的列与目标列表的非垃圾列相匹配。
 */
JunkFilter *
ExecInitJunkFilterConversion(List *fc_targetList,
							 TupleDesc fc_cleanTupType,
							 TupleTableSlot *fc_slot)
{
	JunkFilter *fc_junkfilter;
	int			fc_cleanLength;
	AttrNumber *fc_cleanMap;
	ListCell   *fc_t;
	int			fc_i;

	/*
	 * 使用给定的槽，或者如果未给定则创建一个新槽。
	 */
	if (fc_slot)
		ExecSetSlotDescriptor(fc_slot, fc_cleanTupType);
	else
		fc_slot = MakeSingleTupleTableSlot(fc_cleanTupType, &TTSOpsVirtual);

	/*
	 * 计算原始元组属性与“干净”元组属性之间的映射。
	 *
	 * “映射”是一个“cleanLength”属性编号的数组，即“干净”元组的每个属性一个条目。
	 * 此条目的值是对应于“原始”元组相应属性的属性编号。我们对任何已删除的属性存储
	 * 零，标记输出元组中需要一个 NULL。
	 */
	fc_cleanLength = fc_cleanTupType->natts;
	if (fc_cleanLength > 0)
	{
		fc_cleanMap = (AttrNumber *) palloc0(fc_cleanLength * sizeof(AttrNumber));
		fc_t = list_head(fc_targetList);
		for (fc_i = 0; fc_i < fc_cleanLength; fc_i++)
		{
			if (TupleDescAttr(fc_cleanTupType, fc_i)->attisdropped)
				continue;		/* 映射条目已经是零 */
			for (;;)
			{
				TargetEntry *fc_tle = lfirst(fc_t);

				fc_t = lnext(fc_targetList, fc_t);
				if (!fc_tle->resjunk)
				{
					fc_cleanMap[fc_i] = fc_tle->resno;
					break;
				}
			}
		}
	}
	else
		fc_cleanMap = NULL;

	/*
	 * 最后创建并初始化 JunkFilter 结构。
	 */
	fc_junkfilter = makeNode(JunkFilter);

	fc_junkfilter->jf_targetList = fc_targetList;
	fc_junkfilter->jf_cleanTupType = fc_cleanTupType;
	fc_junkfilter->jf_cleanMap = fc_cleanMap;
	fc_junkfilter->jf_resultSlot = fc_slot;

	return fc_junkfilter;
}

/*
 * ExecFindJunkAttribute
 *
 * 找到指定的垃圾属性在垃圾过滤器的目标列表中的位置，
 * 并返回其 resno。如果未找到则返回 InvalidAttrNumber。
 */
AttrNumber ExecFindJunkAttribute(JunkFilter *fc_junkfilter, const char *fc_attrName)
{
	return ExecFindJunkAttributeInTlist(fc_junkfilter->jf_targetList, fc_attrName);
}

/*
 * ExecFindJunkAttributeInTlist
 *
 * 给定子计划的目标列表查找垃圾属性（不一定
 * 属于 JunkFilter）。
 */
AttrNumber ExecFindJunkAttributeInTlist(List *fc_targetlist, const char *fc_attrName)
{
	ListCell   *fc_t;

	foreach(fc_t, fc_targetlist)
	{
		TargetEntry *fc_tle = lfirst(fc_t);

		if (fc_tle->resjunk && fc_tle->resname &&
			(strcmp(fc_tle->resname, fc_attrName) == 0))
		{
			/* 我们找到了！ */
			return fc_tle->resno;
		}
	}

	return InvalidAttrNumber;
}

/*
 * ExecFilterJunk
 *
 * 构建并返回一个去除所有垃圾属性的槽位。
 */
TupleTableSlot * ExecFilterJunk(JunkFilter *fc_junkfilter, TupleTableSlot *fc_slot)
{
	TupleTableSlot *fc_resultSlot;
	AttrNumber *fc_cleanMap;
	TupleDesc	fc_cleanTupType;
	int			fc_cleanLength;
	int			fc_i;
	Datum	   *fc_values;
	bool	   *fc_isnull;
	Datum	   *fc_old_values;
	bool	   *fc_old_isnull;

	/*
	 * 提取旧元组的所有值。
	 */
	slot_getallattrs(fc_slot);
	fc_old_values = fc_slot->tts_values;
	fc_old_isnull = fc_slot->tts_isnull;

	/*
	 * 从垃圾过滤器获取信息
	 */
	fc_cleanTupType = fc_junkfilter->jf_cleanTupType;
	fc_cleanLength = fc_cleanTupType->natts;
	fc_cleanMap = fc_junkfilter->jf_cleanMap;
	fc_resultSlot = fc_junkfilter->jf_resultSlot;

	/*
	 * 准备构建一个虚拟结果元组。
	 */
	ExecClearTuple(fc_resultSlot);
	fc_values = fc_resultSlot->tts_values;
	fc_isnull = fc_resultSlot->tts_isnull;

	/*
	 * 将数据转置到新元组的适当字段中。
	 */
	for (fc_i = 0; fc_i < fc_cleanLength; fc_i++)
	{
		int			fc_j = fc_cleanMap[fc_i];

		if (fc_j == 0)
		{
			fc_values[fc_i] = (Datum) 0;
			fc_isnull[fc_i] = true;
		}
		else
		{
			fc_values[fc_i] = fc_old_values[fc_j - 1];
			fc_isnull[fc_i] = fc_old_isnull[fc_j - 1];
		}
	}

	/*
	 * 然后返回虚拟元组。
	 */
	return ExecStoreVirtualTuple(fc_resultSlot);
}
